<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Модель для работы с выборкой SQL запроса
 *
 * @package CoreFramework
 * @version 1.0
 * @author Gennadiy Kozlenko
 */
class M_Select extends Model {
   
   /**
    * SQL запрос
    *
    * @access protected
    * @var string
    */
   var $_sql = '';
   
   /**
    * Поля выборки
    *
    * @access private
    * @var array
    */
   var $_fields = array();
   
   /**
    * Массив результатов
    * Если небыло еще выборки, то null
    *
    * @access private
    * @var mixed
    */
   var $_results = null;
   
   /**
    * Текущая позиция в стеке выборки данных
    *
    * @access private
    * @var integer
    */
   var $_position = 0;
   
   /**
    * Общее количество результатов (с учетом ограничений, но без учета лимитации)
    *
    * @access private
    * @var integer
    */
   var $_all_count = 0;
   
   /**
    * Сортировочный массив
    *
    * @access private
    * @var array
    */
   var $_sort = array();
   
   /**
    * Начало диапазона выборки
    *
    * @access private
    * @var integer
    */
   var $_start_range = 0;
   
   /**
    * Размер диапазона выборки
    *
    * @access private
    * @var integer
    */
   var $_limit_range = 0;
   
   /**
    * Конструктор класса
    *
    * @access public
    * @return M_Select
    */
   function M_Select() {
      parent::Model();
   } // end M_Select
   
   /**
    * Установка SQL запроса
    *
    * @access public
    * @param string $table
    */
   function set_sql($sql) {
      $this->clean();
      $this->_sql = $sql;
      // Получаем поля
      $sql = $this->_sql . " LIMIT 0";
      $query = $this->db->query($sql);
      $fields = array();
      foreach ($query->list_fields() as $field) {
         $fields[$field] = array(
            'name'  => $field,
            'title' => '',
            'sort'  => true
         );
      }
      $this->_fields = $fields;
   } // end set_sql
   
   /**
    * Получнние SQL запроса
    *
    * @access public
    * @return string
    */
   function get_sql() {
      return $this->_sql;
   } // end get_sql
   
   /**
    * Получение полей
    *
    * @access public
    * @return array
    */
   function get_fields() {
      return $this->_fields;
   } // end get_fields
   
   /**
    * Очищение данных предыдущей работы
    *
    * @access public
    */
   function clean() {
      $this->clean_results();
      $this->clean_sort();
      $this->set_limit(0);
   } // end clean
   
   /**
    * Очистка результатов
    *
    * @access public
    */
   function clean_results() {
      $this->_results = null;
      $this->_position = 0;
      $this->_all_count = 0;
   } // end clean_results
   
   /**
    * Установка сортировки
    *
    * @access public
    * @deprecated Используйте метод add_sort
    * @param string $field
    * @param boolean $forward
    * @return boolean
    */
   function set_sort($field, $forward = true) {
      $this->clean_sort();
      return $this->add_sort($field, $forward);
   } // end set_sort
   
   /**
    * Установка сортировки
    *
    * @access public
    * @param string $field
    * @param boolean $forward
    * @return boolean
    */
   function add_sort($field, $forward = true) {
      if (!isset($this->_fields[$field])) {
         if ('RAND()' != strtoupper($field)) {
            return false;
         }
      } elseif (!isset($this->_fields[$field]['sort']) || !$this->_fields[$field]['sort']) {
         return false;
      }
      $value = $this->db->escape_str($field) . ' ' . ($forward ? 'ASC' : 'DESC');
      array_push($this->_sort, $value);
      return true;
   } // end add_sort
   
   /**
    * Очистка полей сортировки
    *
    * @access public
    */
   function clean_sort() {
      $this->_sort = array();
   } // end clean_sort
   
   /**
    * Получение полей сортировки
    *
    * @access public
    * @param mixed $index
    * @return mixed
    */
   function get_sort_fields($index = null) {
      if (null !== $index) {
         // Если указан индекс, то возвращаем поле
         $field = false;
         if (isset($this->_sort[$index])) {
            $array = explode(' ', $this->_sort[$index]);
            $field = $array[0];
         }
         return $field;
      }
      // Если не указан нормальный индекс, то возвращаем массив полей
      $fields = array();
      foreach ($this->_sort as $value) {
         $array = explode(' ', $value);
         array_push($fields, $array[0]);
      }
      return $fields;
   } // end get_sort_fields
   
   /**
    * Получение направлений сортировки
    *
    * @access public
    * @param mixed $index
    * @return boolean
    */
   function get_sort_directions($index = null) {
      if (null !== $index) {
         // Если указан индекс, то возвращаем поле
         $direction = false;
         if (isset($this->_sort[$index])) {
            $array = explode(' ', $this->_sort[$index]);
            $direction = 'ASC' == $array[1] ? true : false;
         }
         return $direction;
      }
      // Если не указан нормальный индекс, то возвращаем массив полей
      $directions = array();
      foreach ($this->_sort as $value) {
         $array = explode(' ', $value);
         array_push($directions, 'ASC' == $array[1] ? true : false);
      }
      return $directions;
   } // end get_sort_directions
   
   /**
    * Установка диапазона выборки
    *
    * @param integer $limit
    * @param integer $start
    * @return boolean
    */
   function set_limit($limit, $start = 0) {
      $this->_limit_range = (int) abs($limit);
      $this->_start_range = (int) abs($start);
      return true;
   } // end set_limit
   
   /**
    * Загрузка результатов из базы
    *
    * @access public
    * @return boolean
    */
   function load_results() {
      $this->clean_results();
      if (empty($this->_sql)) {
         return false;
      }
      // Формируем запросы
      $sql = $this->_sql;
      // Добавляем спец штуку для вычисления количества записей
      $sql = preg_replace('~^\s*SELECT~mi', 'SELECT SQL_CALC_FOUND_ROWS', $sql, 1);
      $sql_all_count = "";
      if (0 < count($this->_sort)) {
         $sql .= " ORDER BY " . implode(', ', $this->_sort);
      }
      if (0 < $this->_limit_range) {
         $limit = $this->_build_limit_statement();
         if (!empty($limit)) {
            $sql .= " LIMIT " . $limit;
         }
         $sql_all_count = "SELECT FOUND_ROWS() count";
      }
      // Выполняем запросы
      $query = $this->db->query($sql);
      if (!empty($sql_all_count)) {
         $c_query = $this->db->query($sql_all_count);
         if (0 < $c_query->num_rows()) {
            $result = $c_query->row_array();
            $this->_all_count = (int) $result['count'];
         }
      }
      $this->_results = array();
      // Обрабатываем результаты
      $results = $query->result_array();
      $this->_process_results($results);
      foreach ($results as $result) {
         $this->_process_result($result);
         array_push($this->_results, $result);
      }
      return true;
   } // end load_results
   
   /**
    * Получение результатов
    *
    * @access public
    * @return mixed
    */
   function get_results() {
      if (null === $this->_results) {
         if (!$this->load_results()) {
            return false;
         }
      }
      return $this->_results;
   } // end get_results
   
   /**
    * Получение очередного результата
    *
    * @access public
    * @return mixed
    */
   function next_result() {
      if (null === $this->_results) {
         if (!$this->load_results()) {
            return false;
         }
      }
      if (isset($this->_results[$this->_position])) {
         return $this->_results[$this->_position++];
      }
      return false;
   } // end next_result
   
   /**
    * Получение количества результатов
    *
    * @access public
    * @return mixed
    */
   function get_count() {
      if (null === $this->_results) {
         if (!$this->load_results()) {
            return false;
         }
      }
      return count($this->_results);
   } // end get_count
   
   /**
    * Получение общего количества результатов
    *
    * @access public
    * @return mixed
    */
   function get_all_count() {
      if (null === $this->_results) {
         if (!$this->load_results()) {
            return false;
         }
      }
      return $this->_all_count;
   } // end get_all_count
   
   /**
    * Метод генерации limit части sql запроса
    *
    * @access protected
    * @return string
    */
   function _build_limit_statement() {
      $sql = $this->db->escape_str($this->_start_range) . ", " . $this->db->escape_str($this->_limit_range);
      return $sql;
   } // end _build_limit_statement
   
   /**
    * Обработка результата
    *
    * @access protected
    * @param array $result
    */
   function _process_result(&$result) {
      
   } // end _process_result
   
   /**
    * Обработка результатов
    *
    * @access protected
    * @param array $results
    */
   function _process_results(&$results) {
      
   } // end _process_results
   
}
